// 从v2.11.2起，SDK 支持了 WebSocket，推荐接入；v2.10.2及以下版本，使用 HTTP
import TIM from "tim-js-sdk";
import TIMUploadPlugin from "tim-upload-plugin";
import { uploadFile } from "@/utils/upload.js";
import { showToast, showLoading, hideLoading } from "@/utils/index.js";
import store from "@/store";
import * as permissionCheck from '@/utils/permissionCheck.js'

const options = {
	SDKAppID: 1600079384, // 接入时需要将0替换为您的即时通信 IM 应用的 SDKAppID
};
// 创建 SDK 实例，`TIM.create()`方法对于同一个 `SDKAppID` 只会返回同一份实例
const tim = TIM.create(options); // SDK 实例通常用 tim 表示

// 设置 SDK 日志输出级别，详细分级请参见 <a href="https://web.sdk.qcloud.com/im/doc/zh-cn/SDK.html#setLogLevel">setLogLevel 接口的说明</a>
// tim.setLogLevel(0); // 普通级别，日志量较多，接入时建议使用
tim.setLogLevel(1); // release 级别，SDK 输出关键信息，生产环境时建议使用

// 注册腾讯云即时通信 IM 上传插件
tim.registerPlugin({ "tim-upload-plugin": TIMUploadPlugin });

// 即时通讯类
class IM {
	constructor() {
		// 是否登录状态
		this.loginState = false;
		// 网络状态
		this.netState = "NET_STATE_CONNECTED";
		// 即时通讯状态
		this.imState = "success";
		// sdk是否处于ready状态
		this.sdkReady = false;
		this.tim = tim;
		this.TIM = TIM;
		// 给函数绑定this指向
		this.onSdkReady = this.onSdkReady.bind(this);
		this.onNetStateChange = this.onNetStateChange.bind(this);
		this.onKickedOut = this.onKickedOut.bind(this);
		this.onError = this.onError.bind(this);
		this.onSdkNotReady = this.onSdkNotReady.bind(this);
	}

	/**
	 * @description 即时通讯登录
	 * @param userID
	 * @param userSig
	 * @returns {Promise<unknown>}
	 */
	login(userID, userSig) {
		// 执行登录逻辑
		return new Promise((resolve, reject) => {
			tim
				.login({
					userID,
					userSig,
				})
				.then((res) => {
					console.log("即时通讯登录成功", res.data);
					this.loginState = true;
					if (res.data.repeatLogin === true) {
						// 标识账号已登录，本次登录操作为重复登录。v2.5.1 起支持
						console.log(res.data.errorInfo);
						resolve(res.data);
					} else {
						this.on();
						resolve(res.data);
					}
				})
				.catch((err) => {
					this.loginState = false;
					showToast("即时通讯登录失败，请重试");
					console.log("即时通讯登录失败，请重试", err);
				});
		});
	}

	/**
	 * @description 即时通讯退出登录
	 * @returns {Promise<unknown>}
	 */
	logout() {
		return new Promise((resolve, reject) => {
			tim
				.logout()
				.then((res) => {
					console.log("即时通讯退出登录成功", res.data);
					this.loginState = false;
					// 取消监听所有事件
					this.off();
					resolve(res.data);
				})
				.catch((err) => {
					console.log("即时通讯退出登录失败", err);
					this.loginState = false;
					// 取消监听所有事件
					this.off();
				});
		});
	}

	/**
	 * @description 即时通讯监听事件
	 */
	on() {
		tim.on(TIM.EVENT.SDK_READY, this.onSdkReady);
		tim.on(TIM.EVENT.NET_STATE_CHANGE, this.onNetStateChange);
		tim.on(TIM.EVENT.KICKED_OUT, this.onKickedOut);
		tim.on(TIM.EVENT.ERROR, this.onError);
		tim.on(TIM.EVENT.SDK_NOT_READY, this.onSdkNotReady);
	}

	/**
	 * @description 取消即时通讯监听事件
	 */
	off() {
		tim.off(TIM.EVENT.SDK_READY, this.onSdkReady);
		tim.off(TIM.EVENT.NET_STATE_CHANGE, this.onNetStateChange);
		tim.off(TIM.EVENT.KICKED_OUT, this.onKickedOut);
		tim.off(TIM.EVENT.ERROR, this.onError);
		tim.off(TIM.EVENT.SDK_NOT_READY, this.onSdkNotReady);
	}

	/**
	 * @description 监听网络状态发生改变
	 * @param e
	 */
	onNetStateChange(e) {
		console.log("网络状态改变了", e.data);
		switch (e.data.state) {
			case TIM.TYPES.NET_STATE_CONNECTED:
				this.netState = "NET_STATE_CONNECTED";
				// showToast("已接入网络");
				break;
			case TIM.TYPES.NET_STATE_CONNECTING:
				this.netState = "NET_STATE_CONNECTING";
				// showToast("当前网络不稳定，重新连接中");
				break;
			case TIM.TYPES.NET_STATE_DISCONNECTED:
				this.netState = "NET_STATE_DISCONNECTED";
				// showToast("当前网络不可用");
				break;
			default:
				break;
		}
	}

	/**
	 * @description 监听用户被踢下线
	 * @param e
	 */
	onKickedOut(e) {
		console.log("监听到用户被踢下线", e.data.type);
		switch (e.data.type) {
			case TIM.TYPES.KICKED_OUT_MULT_ACCOUNT:
				showToast("您已在别的地方登录，当前状态已离线");
				break;
			case TIM.TYPES.KICKED_OUT_MULT_DEVICE:
				showToast("您已在别的终端登录，当前状态已离线");
				break;
			case TIM.TYPES.KICKED_OUT_USERSIG_EXPIRED:
				showToast("您的应用签名已过期，当前状态已离线");
				break;
			default:
				showToast("当前状态已离线");
				break;
		}
		// 退出登录
		store.dispatch("resetStore").then(() => {
			uni.reLaunch({
				url: "/pages/login/login",
			});
		});
	}

	/**
	 * @description 监听SDK遇到错误
	 * @param e
	 */
	onError(e) {
		console.log("SDK遇到错误", e.data);
		this.imState = "error";
		showToast(e.data.message);
	}

	/**
	 * @description SDK 进入 not ready 状态时触发，此时接入侧将无法使用 SDK 发送消息等功能。
	 * @description 如果想恢复使用，接入侧需调用 login 接口，驱动 SDK 进入 ready 状态
	 * @param e
	 */
	onSdkNotReady(e) {
		console.log("SDK进入notReady状态", e);
		this.sdkReady = false;
	}

	/**
	 * @description SDK 进入 ready 状态时触发，接入侧监听此事件，然后可调用 SDK 发送消息等API，使用 SDK 的各项功能
	 * @description 注意：login 成功才会驱动 SDK 触发 SDK_READY 事件
	 * @param e
	 */
	onSdkReady(e) {
		console.log("SDK已处于ready状态，可正常使用", e);
		this.sdkReady = true;
	}

	/**
	 * @description 获取会话列表
	 * @returns {Promise<unknown>}
	 */
	getConversationList() {
		return new Promise((resolve, reject) => {
			tim
				.getConversationList()
				.then((res) => {
					const conversationList = res.data.conversationList || [];
					console.log("获取会话列表成功", conversationList);
					resolve(conversationList);
				})
				.catch((err) => {
					console.log("获取会话列表失败", err);
				});
		});
	}

	/**
	 * @description 获取聊天记录
	 * @param conversationID 会话ID 或者群组ID
	 * @param nextReqMessageID 用于下拉加载的ID
	 * @param count 拉取的数量
	 * @returns {Promise<unknown>}
	 */
	getMessageList(conversationID, nextReqMessageID, count = 15) {
		return new Promise((resolve, reject) => {
			tim
				.getMessageList({
					conversationID,
					nextReqMessageID,
					count,
				})
				.then((res) => {
					console.log("获取聊天记录成功", res.data);
					resolve(res.data);
				})
				.catch((err) => {
					console.log("获取聊天记录成功失败", err);
				});
		});
	}

	/**
	 * @description 发送消息失败提示
	 * @returns {Promise<never>}
	 */
	sendError() {
		if (this.imState !== "success") {
			showToast("发送消息失败，SDK遇到错误，请重试");
			return Promise.reject("发送消息失败，SDK遇到错误，请重试");
		}
		if (this.netState !== "NET_STATE_CONNECTED") {
			showToast("发送消息失败，当前网络不可用");
			return Promise.reject("发送消息失败，当前网络不可用");
		}
		if (!this.sdkReady) {
			showToast("发送消息失败，SDK处于not_ready状态，请重新登录");
			return Promise.reject("发送消息失败，SDK处于not_ready状态，请重新登录");
		}
	}

	/**
	 * @description 创建并发送文字消息
	 * @param to 要发送的对象
	 * @param text 发送的文本内容
	 * @param cloudCustomData 自定义消息数据
	 * @returns {Promise<unknown>}
	 */
	sendMessage(to, text, cloudCustomData) {
		this.sendError();
		const createTextMessage = tim.createTextMessage({
			to,
			conversationType: TIM.TYPES.CONV_C2C, // 单聊
			payload: { text },
			cloudCustomData,
		});
		return new Promise((resolve, reject) => {
			tim
				.sendMessage(createTextMessage)
				.then((res) => {
					console.log("发送文本消息成功", res.data);
					resolve(res.data);
				})
				.catch((err) => {
					showToast("发送文本消息失败");
					console.log("发送文本消息失败", err);
				});
		});
	}

	/**
	 * @description 发送图片消息
	 * @param to 要发送的对象
	 * @param extension 自定义消息的 extension 字段
	 * @returns {Promise<unknown>}
	 */
	sendImageMessage(to, extension) {
		this.sendError();
		return new Promise((resolve, reject) => {
			permissionCheck.premissionCheckWrap('CAMERA_EXTERNAL_STORAGE').then(()=>{
				uni.chooseImage({
					count: 1,
					sizeType: ["original", "compressed"],
					sourceType: ["album", "camera"],
					success: async (info) => {
						const tempFilePaths = info.tempFilePaths || [];
						console.log("temp图片路径", tempFilePaths);
						const uploadUrl = await uploadFile(tempFilePaths[0], "图片");
							console.log("图片路径", uploadUrl);
						const res = await this.sendCustomMessage(
							to,
							"image",
							uploadUrl,
							extension
						);
						resolve(res);
					},
				});
			})
		});
	}

	/**
	 * @description 发送视频消息
	 * @param to 要发送的对象
	 * @param extension 自定义消息的 extension 字段
	 * @returns {Promise<unknown>}
	 */
	sendVideoMessage(to, extension) {
		this.sendError();
		return new Promise((resolve, reject) => {
			permissionCheck.premissionCheckWrap('CAMERA_EXTERNAL_STORAGE').then(()=>{
				uni.chooseVideo({
					sourceType: ["album", "camera"],
					maxDuration: 60,
					success: async (info) => {
						console.log("选择的视频文件", info);
						const uploadUrl = await uploadFile(info.tempFilePath || "", "视频");
						const res = await this.sendCustomMessage(
							to,
							"video",
							`${uploadUrl}|${Math.ceil(info.duration || 0)}`,
							extension
						);
						resolve(res);
					},
				});
			})
		});
	}

	/**
	 * @description 创建并发送自定义消息
	 * @param to 要发送的对象
	 * @param data 自定义消息的 data 字段
	 * @param description 自定义消息的 description 字段
	 * @param extension 自定义消息的 extension 字段
	 * @returns {Promise<unknown>}
	 */
	sendCustomMessage(to, data, description, extension) {
		this.sendError();
		const createCustomMessage = tim.createCustomMessage({
			to,
			conversationType: TIM.TYPES.CONV_C2C, // 单聊
			payload: {
				data, // 自定义消息的 data 字段
				description, // 自定义消息的 description 字段
				extension, // 自定义消息的 extension 字段
			},
		});
		return new Promise((resolve, reject) => {
			tim
				.sendMessage(createCustomMessage)
				.then((res) => {
					console.log("发送自定义消息成功", res.data);
					resolve(res.data);
				})
				.catch((err) => {
					showToast("发送自定义消息失败");
					console.log("发送自定义消息失败", err);
				});
		});
	}

	/**
	 * @description 更新个人资料
	 * @param nick
	 * @param avatar
	 * @returns {Promise<unknown>}
	 */
	updateMyProfile(nick, avatar) {
		return new Promise((resolve, reject) => {
			tim
				.updateMyProfile({
					nick,
					avatar,
				})
				.then((res) => {
					console.log("更新个人资料成功", res.data);
					resolve(res.data);
				})
				.catch((err) => {
					console.log("更新个人资料失败", err);
				});
		});
	}

	/**
	 * @description 消息已读上报
	 * @param conversationID
	 */
	setMessageRead(conversationID) {
		tim
			.setMessageRead({ conversationID })
			.then((res) => {
				// 已读上报成功，指定 ID 的会话的 unreadCount 属性值被置为0
				console.log("消息已读上报成功", res);
			})
			.catch((err) => {
				console.log("消息已读上报失败", err);
			});
	}

	/**
	 * @description 删除消息
	 * @param messageList 要删除的消息实例列表
	 */
	deleteMessage(messageList) {
		return new Promise((resolve, reject) => {
			tim
				.deleteMessage(messageList)
				.then((res) => {
					console.log("删除消息成功", res.data);
					resolve(res.data);
				})
				.catch((err) => {
					showToast("删除消息失败");
					console.log("删除消息失败", err);
				});
		});
	}

	/**
	 * @description 撤回消息
	 * @param message 要撤回的消息实例
	 */
	revokeMessage(message) {
		return new Promise((resolve, reject) => {
			tim
				.revokeMessage(message)
				.then((res) => {
					console.log("撤回消息成功", res);
					resolve(res);
				})
				.catch((err) => {
					showToast((err + "").replace(/Error: |。/g, ""));
					console.log("撤回消息失败", err);
				});
		});
	}
}

export default new IM();
